using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security;

namespace DeSleeper.Client.Installation
{
    public class InstallTargets
    {
        private string _input;
        private string[] _allTargets;
        public IEnumerable<string> AllTargets
        {
            get
            {
                if (_allTargets == null)
                {
                    _allTargets = _input.Split(new[] {',', ' '}, StringSplitOptions.RemoveEmptyEntries);
                }
                return _allTargets;
            }
        }
        private bool? _includesLocal;
        public bool IncludesLocal
        {
            get
            {
                if (!_includesLocal.HasValue)
                {
                    _includesLocal = AllTargets.Any(target =>
                                                 string.Equals(target, "localhost",
                                                               StringComparison.InvariantCultureIgnoreCase));
                }
                return _includesLocal.Value;
            }
        }

        private void parseRemoteTargets()
        {
            _categorizationErrors = new Dictionary<string, Exception>();
            var groupedMachines = AllTargets
                .Where(installTarget =>
                       !string.Equals(installTarget, "localhost", StringComparison.InvariantCultureIgnoreCase))
                .GroupBy<string, bool?>(tryCheckDotNetInstalled).ToList();

            _compatibleRemote = groupedMachines.SingleOrDefault(group => group.Key.GetValueOrDefault(false));
            _incompatibleRemote = groupedMachines.SingleOrDefault(group => !group.Key.GetValueOrDefault(true));

            if (_compatibleRemote == null) _compatibleRemote = new List<string>();
            if (_incompatibleRemote == null) _incompatibleRemote = new List<string>();
        }

        private bool? tryCheckDotNetInstalled(string machineName)
        {
            try
            {
                return Installer.CheckDotNetInstalled(machineName);
            }
            catch (UnauthorizedAccessException exception)
            {
                _categorizationErrors.Add(machineName, exception);
                return null;
            }
            catch (IOException exception)
            {
                _categorizationErrors.Add(machineName, exception);
                return null;
            }
            catch (SecurityException exception)
            {
                _categorizationErrors.Add(machineName, exception);
                return null;
            }
        }

        private IEnumerable<string> _compatibleRemote;
        public IEnumerable<string> CompatibleRemote
        {
            get
            {
                if (_compatibleRemote == null) parseRemoteTargets();
                return _compatibleRemote;
            }
        }

        public IEnumerable<string> CompatibleAndUnknownRemote
        {
            get
            {
                return Enumerable.Union(CompatibleRemote, CategorizationErrors.Keys);
            }
        }



        private IEnumerable<string> _incompatibleRemote;
        public IEnumerable<string> IncompatibleRemote
        {
            get
            {
                if (_incompatibleRemote == null) parseRemoteTargets();
                return _incompatibleRemote;
            }
        }

        private IDictionary<string, Exception> _categorizationErrors;
        public IDictionary<string, Exception> CategorizationErrors
        {
            get
            {
                if (_categorizationErrors == null) parseRemoteTargets();
                return _categorizationErrors;
            }
        }

        public InstallTargets(string input)
        {
            _input = input;

            if (!string.IsNullOrEmpty(input))
                _includesLocal = true;
        }
    }
}